package org.study.minio.boot.controller;

import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.study.minio.boot.config.MinioConstant;

import java.io.*;
import java.nio.file.Files;

/**
 * 大文件分片上传：每个分片的数据都存储在磁盘，分片上传完毕后再合并，同时删除分片文件
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2023年12月14日
 */
@Slf4j
@CrossOrigin
@RestController
@RequestMapping("/disk")
public class FileUploadDiskController {
    // 上传文件的根目录
    private static final String UPLOAD_DIR = MinioConstant.DESKTOP + File.separator + "uploads";

    @RequestMapping
    public ModelAndView toChatGpt1() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("upload1");
        return modelAndView;
    }

    @PostMapping("/chunks")
    public ResponseEntity<String> uploadChunk(@RequestParam("file") MultipartFile file,
                                              @RequestParam("chunkNumber") int chunkNumber,
                                              @RequestParam("totalChunks") int totalChunks,
                                              @RequestParam("identifier") String identifier) throws IOException {
        // 创建上传目录
        File uploadDir = new File(UPLOAD_DIR, identifier);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        // 构建分片文件路径
        File chunkFile = new File(uploadDir, identifier + "-" + chunkNumber);
        try (FileOutputStream fos = new FileOutputStream(chunkFile)) {
            // 将分片写入文件
            fos.write(file.getBytes());
        } catch (IOException e) {
            return new ResponseEntity<>("上传分片文件异常：" + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
        if (chunkNumber == totalChunks - 1) {
            // 如果是最后一个分片，进行文件合并
            if (this.mergeChunks(uploadDir, identifier, IdUtil.getSnowflakeNextIdStr() + "_" + identifier)) {
                // 删除分片目录
                this.deleteDirectory(uploadDir);
                return new ResponseEntity<>("合并分片成功", HttpStatus.OK);
            } else {
                return new ResponseEntity<>("合并分片失败", HttpStatus.INTERNAL_SERVER_ERROR);
            }
        }
        return new ResponseEntity<>("上传分片文件成功", HttpStatus.OK);
    }


    /**
     * 合并上传目录中的所有分片文件成为一个完整的文件
     *
     * @param uploadDir  上传目录
     * @param identifier 文件标识符，用于识别同一个文件的所有分片
     * @param filename   文件名
     * @return 合并是否成功
     */
    private boolean mergeChunks(File uploadDir, String identifier, String filename) throws IOException {
        // 创建最终合并后的文件
        File mergedFile = new File(UPLOAD_DIR, filename);
        try (FileOutputStream fos = new FileOutputStream(mergedFile, true)) {
            File[] listFiles = uploadDir.listFiles();
            // 循环遍历分片文件并逐一合并
            for (int i = 0; i < listFiles.length; i++) {
                File chunk = new File(uploadDir, identifier + "-" + i);
                // 将分片文件内容追加到最终合并文件中
                Files.copy(chunk.toPath(), fos);
                // 删除已合并的分片文件
                chunk.delete();
            }
            log.info("文件合并后的位置：" + mergedFile.getAbsolutePath());
            return true;
        }
    }

    /**
     * 删除指定目录及其包含的所有文件和子目录
     *
     * @param directory 要删除的目录
     * @return 删除是否成功
     */
    private boolean deleteDirectory(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                // 递归删除目录中的所有文件和子目录
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
        }
        // 删除目录本身
        return directory.delete();
    }
}
